/*
 * Copyright (C) 2012-2013  Romain DUBOIS
 * 
 * This file is part of NeMoS - Network and Modular Software
 * 
 * NeMoS is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * NeMoS is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with NeMoS.  If not, see <http://www.gnu.org/licenses/>.
 */
package nemos.util.i18n;

import java.text.MessageFormat;
import java.util.Locale;
import java.util.MissingResourceException;
import java.util.ResourceBundle;

import nemos.util.osgi.BundleClassLoader;

import org.osgi.framework.Bundle;
import org.osgi.framework.Constants;
import org.osgi.framework.FrameworkUtil;

/**
 * BundleLocalizer : The OSGi localization support class.
 * 
 * <p>
 * This class looks for the bundle localization files and provides the
 * translation labels. If locale is not provided to the constructor, the default
 * Locale will be checked for each call to the {@link #getValue(String)} method.
 * </p>
 * 
 * @author Romain DUBOIS
 */
public class BundleLocalization implements ITranslator {

    /** The resource bundle (when Locale is set) */
    private ResourceBundle mResources;
    /** The bundle */
    private final Bundle mBundle;

    /**
     * Constructor
     * 
     * @param pClass
     *            A class from the bundle
     * @param pLocale
     *            The locale to use
     */
    public BundleLocalization(final Class<?> pClass, final Locale pLocale) {
        this(FrameworkUtil.getBundle(pClass), pLocale);
    }

    /**
     * Constructor
     * 
     * @param pBundle
     *            The bundle
     * @param pLocale
     *            The locale to use
     */
    public BundleLocalization(final Bundle pBundle, final Locale pLocale) {
        super();

        mBundle = pBundle;
        mResources = getResources(pLocale);
    }

    /**
     * Constructor
     * 
     * @param pClass
     *            A class from the bundle
     */
    public BundleLocalization(final Class<?> pClass) {
        this(FrameworkUtil.getBundle(pClass));
    }

    /**
     * Constructor
     * 
     * @param pBundle
     *            The bundle
     */
    public BundleLocalization(final Bundle pBundle) {
        super();
        mBundle = pBundle;
    }

    /**
     * {@inheritDoc}
     * 
     * @see nemos.util.i18n.ITranslator#getLabel(java.lang.String,
     *      java.lang.Object[])
     */
    @Override
    public String getLabel(final String pKey, final Object... pParams) {
        return translateMessage(null, pKey, null, pParams);
    }

    /**
     * {@inheritDoc}
     * 
     * @see nemos.util.i18n.ITranslator#getMessage(java.lang.String,
     *      java.lang.Object[])
     */
    @Override
    public String getMessage(final String pKey, final Object... pParams) {
        return translateMessage(null, pKey, null, pParams);
    }

    /**
     * {@inheritDoc}
     * 
     * @see nemos.util.i18n.ITranslator#translateLabel(java.lang.String,
     *      java.lang.String, java.util.Locale, java.lang.Object[])
     */
    @Override
    public String translateLabel(final String pBaseName, final String pKey, final Locale pLocale,
            final Object... pParams) {
        return translateMessage(pBaseName, pKey, pLocale, pParams);
    }

    /**
     * {@inheritDoc}
     * 
     * @see nemos.util.i18n.ITranslator#translateMessage(java.lang.String,
     *      java.lang.String, java.util.Locale, java.lang.Object[])
     */
    @Override
    public String translateMessage(final String pBaseName, final String pKey, final Locale pLocale,
            final Object... pParams) {

        // Get resources bundle
        ResourceBundle lResources;
        if (pLocale == null) {
            lResources = getResources();
        } else {
            lResources = getResources(pLocale);
        }

        // Get label with raw key
        String lLabel = get(pKey, lResources);
        // Then try compound keys
        if (lLabel == null && pBaseName != null) {
            lLabel = get(pBaseName + '.' + pKey, lResources);
        }
        if (lLabel == null && pBaseName != null) {
            lLabel = get(pBaseName + '-' + pKey, lResources);
        }

        // Format
        if (lLabel == null) {
            return pKey;
        }
        if (pLocale == null) {
            return MessageFormat.format(lLabel, pParams);
        }
        final MessageFormat lFormat = new MessageFormat(lLabel, pLocale);
        return lFormat.format(pParams);
    }

    /**
     * Get the label for the specified key.
     * 
     * @param pKey
     *            The message key
     * @return The localized message, or <code>null</code> if not found
     */
    public String getValue(final String pKey) {
        return get(pKey, getResources());
    }

    /**
     * Get the resource bundle for the appropriate Locale.
     * 
     * @return The resource bundle
     */
    private ResourceBundle getResources() {
        if (mResources == null) {
            return getResources(Locale.getDefault());
        }
        return mResources;
    }

    /**
     * Get the resource bundle for the provided locale.
     * 
     * @param pLocale
     *            The required locale
     * @return The resource bundle
     */
    private ResourceBundle getResources(final Locale pLocale) {
        String lLocalizationFileName = mBundle.getHeaders().get(Constants.BUNDLE_LOCALIZATION);
        if (lLocalizationFileName == null) {
            lLocalizationFileName = Constants.BUNDLE_LOCALIZATION_DEFAULT_BASENAME;
        }

        try {
            return ResourceBundle.getBundle(lLocalizationFileName, pLocale, new BundleClassLoader(
                    mBundle));
        } catch (final MissingResourceException e) {
            return null;
        }
    }

    /**
     * Get the message in the specified resources.
     * 
     * @param pKey
     *            The message key
     * @param pResources
     *            The resources to use
     * @return The message
     */
    private String get(final String pKey, final ResourceBundle pResources) {
        if (pResources == null) {
            return null;
        }
        try {
            return pResources.getString(pKey);
        } catch (final MissingResourceException e) {
            return null;
        }
    }
}
